"""标准化接口模块
====================
本模块为所有 **MCPTools** 中注册的工具自动生成 **同名异步函数**，
用于在项目内直接调用，而无需了解底层 `MCPTools.call_tool` 细节。

设计目标
---------
1. **功能一致**    ：包装函数仅调用 `MCPTools.call_tool`，保证行为与 AI 调用一致。
2. **输入/输出一致**：不对参数或返回值做任何转换，完全镜像原始接口。
3. **线程安全**    ：借助 `asyncio.Lock` 保护初始化逻辑，避免并发竞态。
4. **向后兼容**    ：不修改现有核心代码，可随时 `refresh_wrappers()` 重新生成。

使用示例
--------
```python
from src.mcp.standard_interface import memory_search

result = await memory_search(query="hello", user_id="u123")
```

额外提供 `set_orchestrator()` 方便在测试或自定义场景下注入外部 orchestrator。
"""

from __future__ import annotations

import asyncio
from typing import Any, Dict, Optional, Callable
from loguru import logger

from src.core.context_orchestrator import ContextOrchestrator
from .tools import MCPTools

# ------------------------
# 全局单例及并发控制
# ------------------------

_LOCK: asyncio.Lock = asyncio.Lock()
_INITIALIZED: bool = False
_ORCHESTRATOR: Optional[ContextOrchestrator] = None
_TOOLS: Optional[MCPTools] = None


authored_wrappers: Dict[str, Callable] = {}


async def _ensure_initialized(config: Optional[Dict[str, Any]] = None) -> None:
    """确保 orchestrator 与工具集合已初始化。

    初始化过程中使用全局锁，保证并发安全。"""
    global _INITIALIZED, _ORCHESTRATOR, _TOOLS

    if _INITIALIZED:
        return

    async with _LOCK:
        if _INITIALIZED:
            return  # 双重检查

        # 若外部未注入 orchestrator，则构造默认实例
        if _ORCHESTRATOR is None:
            cfg = config or {}
            _ORCHESTRATOR = ContextOrchestrator(cfg)  # type: ignore[arg-type]
        
        _TOOLS = MCPTools(_ORCHESTRATOR)

        # 生成包装函数
        _generate_wrappers()

        _INITIALIZED = True
        logger.info("standard_interface 初始化完成")


# ------------------------
# 对外辅助 API
# ------------------------


def set_orchestrator(orchestrator: ContextOrchestrator) -> None:
    """手动注入自定义 orchestrator（多用于测试）。

    调用该函数后会强制刷新包装函数，确保使用新的 orchestrator。"""
    global _ORCHESTRATOR, _TOOLS, _INITIALIZED
    _ORCHESTRATOR = orchestrator
    _TOOLS = MCPTools(orchestrator)
    _INITIALIZED = True
    authored_wrappers.clear()
    _generate_wrappers()
    logger.info("standard_interface 使用自定义 orchestrator 重新初始化")


def refresh_wrappers() -> None:
    """重新生成包装函数（例如新增工具后调用）。"""
    if _TOOLS is None:
        raise RuntimeError("standard_interface 未初始化，无法刷新包装函数")
    authored_wrappers.clear()
    _generate_wrappers()


# ------------------------
# 核心实现：动态生成包装函数
# ------------------------


def _generate_wrappers() -> None:  # noqa: C901 (复杂度由工具数量决定)
    """根据 `_TOOLS` 中的工具动态创建包装函数并导出到模块级命名空间。"""
    if _TOOLS is None:
        raise RuntimeError("工具未就绪，无法生成包装函数")

    tool_list = _TOOLS.get_tool_list()

    for tool_meta in tool_list:
        name: str = tool_meta["name"]
        description: str = tool_meta["description"]
        input_schema: Dict[str, Any] = tool_meta["inputSchema"]["properties"]  # type: ignore[index]

        if name in authored_wrappers:
            continue  # 避免重复生成

        async def _wrapper(*, _tool_name: str = name, **kwargs: Any):  # type: ignore[no-redef]
            await _ensure_initialized()
            return await _TOOLS.call_tool(_tool_name, kwargs)  # type: ignore[arg-type]

        # 构造文档字符串
        params_doc = "\n".join(
            f"    {k}: {v.get('description', '')}" for k, v in input_schema.items()
        ) or "    无"
        _wrapper.__doc__ = (
            f"{description}\n\n参数:\n{params_doc}\n\n返回值:\n    同 `MCPTools.call_tool('{name}', ...)`。"
        )
        _wrapper.__name__ = name  # 保持函数名一致

        globals()[name] = _wrapper  # 暴露到模块级命名空间
        authored_wrappers[name] = _wrapper
        logger.debug(f"生成包装函数: {name}")


# ------------------------
# 在模块导入时进行懒初始化
# ------------------------

# 无需立即初始化，首次调用包装函数时自动初始化。

# 公开名称（供 `from src.mcp.standard_interface import *` 使用）
__all__ = ["set_orchestrator", "refresh_wrappers"]